#include "icmp.h"
#include "ip.h"
#include <string.h>
#include <stdio.h>

/**
 * @brief 处理一个收到的数据包
 *        你首先要检查ICMP报头长度是否小于icmp头部长度
 *        接着，查看该报文的ICMP类型是否为回显请求，
 *        如果是，则回送一个回显应答（ping应答），需要自行封装应答包。
 * 
 *        应答包封装如下：
 *        首先调用buf_init()函数初始化txbuf，然后封装报头和数据，
 *        数据部分可以拷贝来自接收到的回显请求报文中的数据。
 *        最后将封装好的ICMP报文发送到IP层。  
 * 
 * @param buf 要处理的数据包
 * @param src_ip 源ip地址
 */
void icmp_in(buf_t *buf, uint8_t *src_ip)
{
    printf("icmp: icmp_in\n ");
    if(buf->len < sizeof(icmp_hdr_t)){
        printf("icmp: buf smaller than icmp head\n ");
        return;
    }
    icmp_hdr_t* head = buf->data;
    if(!(head->type == ICMP_TYPE_ECHO_REQUEST &&
        head->code == 0)){
        printf("icmp: not echo request\n ");
        return;
    }
    //!!!!!ATTENTION: id and seq may cause error!!!!!!!!!!!!!!!!!!!!!!!
    uint16_t id = head->id;
    uint16_t seq = head->seq;
    // echo reply
    buf_t txbuf;
    buf_copy(&txbuf,buf);
    head = txbuf.data;
    head->type = ICMP_TYPE_ECHO_REPLY;
    head->code = 0;
    head->id = id;
    head->seq = seq;
    head->checksum = 0;
    head->checksum = checksum16(head,(txbuf.len));
    printf("icmp: icmp_in head->checksum %x\n",head->checksum);

    uint8_t tip[NET_IP_LEN];
    memcpy(tip,src_ip,NET_IP_LEN);
    ip_out(&txbuf,tip,NET_PROTOCOL_ICMP);
    // ip_out(buf,tip,NET_PROTOCOL_ICMP);
}

/**
 * @brief 发送icmp不可达
 *        你需要首先调用buf_init初始化buf，长度为ICMP头部 + IP头部 + 原始IP数据报中的前8字节 
 *        填写ICMP报头首部，类型值为目的不可达
 *        填写校验和
 *        将封装好的ICMP数据报发送到IP层。
 * 
 * @param recv_buf 收到的ip数据包
 * @param src_ip 源ip地址
 * @param code icmp code，协议不可达或端口不可达
 */
void icmp_unreachable(buf_t *recv_buf, uint8_t *src_ip, icmp_code_t code)
{
    ip_hdr_t *iphead = recv_buf->data;
    uint8_t *data = recv_buf->data;
    int length = (iphead->hdr_len)*4 + 8;
    buf_t txbuf;
    buf_init(&txbuf,sizeof(icmp_hdr_t)+length);
    icmp_hdr_t* head = txbuf.data;

    head->type = ICMP_TYPE_UNREACH;
    head->code = code;
    head->id = 0;
    head->seq = 0;
    //fill data
    for(int i=0; i<length; i++){
        txbuf.data[sizeof(icmp_hdr_t)+i] = data[i];
    }
    head->checksum = 0;
    head->checksum = checksum16(head,(txbuf.len));
    uint8_t tip[NET_IP_LEN];
    memcpy(tip,src_ip,NET_IP_LEN);
    ip_out(&txbuf,tip,NET_PROTOCOL_ICMP);
    
}